 /*******************************************************************************
  * Copyright (c) 2006, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal.texteditor;


 import java.util.HashMap ;
 import java.util.Iterator ;
 import java.util.Map ;

 import org.eclipse.core.runtime.Assert;

 import org.eclipse.swt.graphics.Font;
 import org.eclipse.swt.graphics.RGB;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;

 import org.eclipse.jface.text.ITextViewer;
 import org.eclipse.jface.text.revisions.IRevisionRulerColumn;
 import org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension;
 import org.eclipse.jface.text.revisions.RevisionInformation;
 import org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension.RenderingMode;
 import org.eclipse.jface.text.source.AnnotationModel;
 import org.eclipse.jface.text.source.CompositeRuler;
 import org.eclipse.jface.text.source.IAnnotationHover;
 import org.eclipse.jface.text.source.IAnnotationModel;
 import org.eclipse.jface.text.source.IAnnotationModelExtension;
 import org.eclipse.jface.text.source.IChangeRulerColumn;
 import org.eclipse.jface.text.source.ILineDifferExtension;
 import org.eclipse.jface.text.source.ILineDifferExtension2;
 import org.eclipse.jface.text.source.ISharedTextColors;
 import org.eclipse.jface.text.source.ISourceViewer;
 import org.eclipse.jface.text.source.IVerticalRulerColumn;
 import org.eclipse.jface.text.source.IVerticalRulerInfo;
 import org.eclipse.jface.text.source.IVerticalRulerInfoExtension;
 import org.eclipse.jface.text.source.IVerticalRulerListener;
 import org.eclipse.jface.text.source.LineNumberChangeRulerColumn;
 import org.eclipse.jface.text.source.LineNumberRulerColumn;

 import org.eclipse.ui.editors.text.EditorsUI;

 import org.eclipse.ui.internal.editors.text.EditorsPlugin;

 import org.eclipse.jface.dialogs.MessageDialogWithToggle;
 import org.eclipse.jface.preference.IPreferenceStore;
 import org.eclipse.jface.preference.PreferenceConverter;
 import org.eclipse.jface.util.IPropertyChangeListener;
 import org.eclipse.jface.util.PropertyChangeEvent;
 import org.eclipse.jface.viewers.ISelectionProvider;
 import org.eclipse.jface.window.Window;
 import org.eclipse.ui.texteditor.AbstractDecoratedTextEditor;
 import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
 import org.eclipse.ui.texteditor.AbstractTextEditor;
 import org.eclipse.ui.texteditor.AnnotationPreference;
 import org.eclipse.ui.texteditor.ITextEditor;
 import org.eclipse.ui.texteditor.ITextEditorExtension;
 import org.eclipse.ui.texteditor.ITextEditorExtension2;
 import org.eclipse.ui.texteditor.MarkerAnnotationPreferences;
 import org.eclipse.ui.texteditor.quickdiff.QuickDiff;
 import org.eclipse.ui.texteditor.rulers.AbstractContributedRulerColumn;

 /**
  * The line number ruler contribution. Encapsulates a {@link LineNumberChangeRulerColumn} as a
  * contribution to the <code>rulerColumns</code> extension point. Instead of instantiating the
  * delegate itself, it calls <code>createLineNumberRulerColumn()</code> in
  * {@link AbstractDecoratedTextEditor} via {@link ICompatibilityForwarder} to maintain compatibility
  * with previous releases.
  *
  * @since 3.3
  */
 public class LineNumberColumn extends AbstractContributedRulerColumn implements IVerticalRulerInfo, IVerticalRulerInfoExtension {
     /**
      * Forwarder for preference checks and ruler creation. Needed to maintain the forwarded APIs in
      * {@link AbstractDecoratedTextEditor}.
      */
     public static interface ICompatibilityForwarder {
         IVerticalRulerColumn createLineNumberRulerColumn();
         boolean isQuickDiffEnabled();
         boolean isLineNumberRulerVisible();
     }
     
     /** The contribution id of the line number / change ruler. */
     public static final String ID= "org.eclipse.ui.editors.columns.linenumbers"; //$NON-NLS-1$

     private static final String FG_COLOR_KEY= AbstractDecoratedTextEditorPreferenceConstants.EDITOR_LINE_NUMBER_RULER_COLOR;
     private static final String BG_COLOR_KEY= AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND;
     private static final String USE_DEFAULT_BG_KEY= AbstractTextEditor.PREFERENCE_COLOR_BACKGROUND_SYSTEM_DEFAULT;
     private final static String LINE_NUMBER_KEY= AbstractDecoratedTextEditorPreferenceConstants.EDITOR_LINE_NUMBER_RULER;
     private final static String REVISION_ASK_BEFORE_QUICKDIFF_SWITCH_KEY= AbstractDecoratedTextEditorPreferenceConstants.REVISION_ASK_BEFORE_QUICKDIFF_SWITCH;

     /**
      * The delegate and implemenation of the ruler.
      */
     private IVerticalRulerColumn fDelegate;
     /**
      * The annotation preferences.
      */
     private final MarkerAnnotationPreferences fAnnotationPreferences= EditorsPlugin.getDefault().getMarkerAnnotationPreferences();

     /**
      * Preference dispatcher that registers a single listener so we don't have to manage every
      * single preference listener.
      */
     private PropertyEventDispatcher fDispatcher;
     private ISourceViewer fViewer;
     private ICompatibilityForwarder fForwarder;

     /*
      * @see org.eclipse.jface.text.source.IVerticalRulerColumn#createControl(org.eclipse.jface.text.source.CompositeRuler, org.eclipse.swt.widgets.Composite)
      */
     public Control createControl(CompositeRuler parentRuler, Composite parentControl) {
         Assert.isTrue(fDelegate != null);
         ITextViewer viewer= parentRuler.getTextViewer();
         Assert.isLegal(viewer instanceof ISourceViewer);
         fViewer= (ISourceViewer) viewer;
         initialize();
         Control control= fDelegate.createControl(parentRuler, parentControl);
         return control;
     }

     /*
      * @see org.eclipse.jface.text.source.IVerticalRulerColumn#getControl()
      */
     public Control getControl() {
         return fDelegate.getControl();
     }

     /*
      * @see org.eclipse.jface.text.source.IVerticalRulerColumn#getWidth()
      */
     public int getWidth() {
         return fDelegate.getWidth();
     }

     /*
      * @see org.eclipse.jface.text.source.IVerticalRulerColumn#redraw()
      */
     public void redraw() {
         fDelegate.redraw();
     }

     /*
      * @see org.eclipse.jface.text.source.IVerticalRulerColumn#setFont(org.eclipse.swt.graphics.Font)
      */
     public void setFont(Font font) {
         fDelegate.setFont(font);
     }

     /*
      * @see org.eclipse.jface.text.source.IVerticalRulerColumn#setModel(org.eclipse.jface.text.source.IAnnotationModel)
      */
     public void setModel(IAnnotationModel model) {
         if (getQuickDiffPreference())
             fDelegate.setModel(model);
     }
     
     /*
      * @see org.eclipse.jface.text.source.IVerticalRulerInfo#getLineOfLastMouseButtonActivity()
      */
     public int getLineOfLastMouseButtonActivity() {
         if (fDelegate instanceof IVerticalRulerInfo)
             ((IVerticalRulerInfo) fDelegate).getLineOfLastMouseButtonActivity();
         return -1;
     }

     /*
      * @see org.eclipse.jface.text.source.IVerticalRulerInfo#toDocumentLineNumber(int)
      */
     public int toDocumentLineNumber(int y_coordinate) {
         if (fDelegate instanceof IVerticalRulerInfo)
             ((IVerticalRulerInfo) fDelegate).toDocumentLineNumber(y_coordinate);
         return -1;
     }

     /*
      * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#addVerticalRulerListener(org.eclipse.jface.text.source.IVerticalRulerListener)
      */
     public void addVerticalRulerListener(IVerticalRulerListener listener) {
         if (fDelegate instanceof IVerticalRulerInfoExtension)
             ((IVerticalRulerInfoExtension) fDelegate).addVerticalRulerListener(listener);
     }

     /*
      * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#getHover()
      */
     public IAnnotationHover getHover() {
         if (fDelegate instanceof IVerticalRulerInfoExtension)
             return ((IVerticalRulerInfoExtension) fDelegate).getHover();
         return null;
     }

     /*
      * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#getModel()
      */
     public IAnnotationModel getModel() {
         if (fDelegate instanceof IVerticalRulerInfoExtension)
             return ((IVerticalRulerInfoExtension) fDelegate).getModel();
         return null;
     }

     /*
      * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#removeVerticalRulerListener(org.eclipse.jface.text.source.IVerticalRulerListener)
      */
     public void removeVerticalRulerListener(IVerticalRulerListener listener) {
         if (fDelegate instanceof IVerticalRulerInfoExtension)
             ((IVerticalRulerInfoExtension) fDelegate).removeVerticalRulerListener(listener);
     }

     /*
      * @see org.eclipse.ui.texteditor.rulers.AbstractContributedRulerColumn#columnRemoved()
      */
     public void columnRemoved() {
         if (fDispatcher != null) {
             fDispatcher.dispose();
             fDispatcher= null;
         }
     }

     private IPreferenceStore getPreferenceStore() {
         return EditorsUI.getPreferenceStore();
     }

     private ISharedTextColors getSharedColors() {
         return EditorsUI.getSharedTextColors();
     }

     /**
      * Initializes the given line number ruler column from the preference store.
      */
     private void initialize() {
         final IPreferenceStore store= getPreferenceStore();
         if (store == null)
             return;

         // initial set up
 updateForegroundColor(store, fDelegate);
         updateBackgroundColor(store, fDelegate);
         
         updateLineNumbersVisibility(fDelegate);
         updateQuickDiffVisibility(fDelegate);
         updateCharacterMode(store, fDelegate);
         updateRevisionRenderingMode(store, fDelegate);
         updateRevisionAuthorVisibility(store, fDelegate);
         updateRevisionIdVisibility(store, fDelegate);

         Map annotationPrefs= getAnnotationPreferenceMap();
         final AnnotationPreference changedPref= (AnnotationPreference) annotationPrefs.get("org.eclipse.ui.workbench.texteditor.quickdiffChange"); //$NON-NLS-1$
 final AnnotationPreference addedPref= (AnnotationPreference) annotationPrefs.get("org.eclipse.ui.workbench.texteditor.quickdiffAddition"); //$NON-NLS-1$
 final AnnotationPreference deletedPref= (AnnotationPreference) annotationPrefs.get("org.eclipse.ui.workbench.texteditor.quickdiffDeletion"); //$NON-NLS-1$
 updateChangedColor(changedPref, store, fDelegate);
         updateAddedColor(addedPref, store, fDelegate);
         updateDeletedColor(deletedPref, store, fDelegate);

         fDelegate.redraw();

         // listen to changes
 fDispatcher= new PropertyEventDispatcher(store);

         fDispatcher.addPropertyChangeListener(FG_COLOR_KEY, new IPropertyChangeListener() {
             public void propertyChange(PropertyChangeEvent event) {
                 updateForegroundColor(store, fDelegate);
                 fDelegate.redraw();
             }
         });
         IPropertyChangeListener backgroundHandler= new IPropertyChangeListener() {
             public void propertyChange(PropertyChangeEvent event) {
                 updateBackgroundColor(store, fDelegate);
                 fDelegate.redraw();
             }
         };
         fDispatcher.addPropertyChangeListener(BG_COLOR_KEY, backgroundHandler);
         fDispatcher.addPropertyChangeListener(USE_DEFAULT_BG_KEY, backgroundHandler);

         fDispatcher.addPropertyChangeListener(LINE_NUMBER_KEY, new IPropertyChangeListener() {
             public void propertyChange(PropertyChangeEvent event) {
                 // only handle quick diff on/off information, but not ruler visibility (handled by AbstractDecoratedTextEditor)
 updateLineNumbersVisibility(fDelegate);
             }
         });

         fDispatcher.addPropertyChangeListener(AbstractDecoratedTextEditorPreferenceConstants.QUICK_DIFF_CHARACTER_MODE, new IPropertyChangeListener() {
             public void propertyChange(PropertyChangeEvent event) {
                 updateCharacterMode(store, fDelegate);
             }
         });

         fDispatcher.addPropertyChangeListener(AbstractDecoratedTextEditorPreferenceConstants.REVISION_RULER_RENDERING_MODE, new IPropertyChangeListener() {
             public void propertyChange(PropertyChangeEvent event) {
                 updateRevisionRenderingMode(store, fDelegate);
             }
         });
         
         fDispatcher.addPropertyChangeListener(AbstractDecoratedTextEditorPreferenceConstants.REVISION_RULER_SHOW_AUTHOR, new IPropertyChangeListener() {
             public void propertyChange(PropertyChangeEvent event) {
                 updateRevisionAuthorVisibility(store, fDelegate);
             }
         });
         
         fDispatcher.addPropertyChangeListener(AbstractDecoratedTextEditorPreferenceConstants.REVISION_RULER_SHOW_REVISION, new IPropertyChangeListener() {
             public void propertyChange(PropertyChangeEvent event) {
                 updateRevisionIdVisibility(store, fDelegate);
             }
         });
         
         fDispatcher.addPropertyChangeListener(AbstractDecoratedTextEditorPreferenceConstants.QUICK_DIFF_ALWAYS_ON, new IPropertyChangeListener() {
             public void propertyChange(PropertyChangeEvent event) {
                 updateQuickDiffVisibility(fDelegate);
             }
         });

         if (changedPref != null) {
             fDispatcher.addPropertyChangeListener(changedPref.getColorPreferenceKey(), new IPropertyChangeListener() {
                 public void propertyChange(PropertyChangeEvent event) {
                     updateChangedColor(changedPref, store, fDelegate);
                     fDelegate.redraw();
                 }
             });
         }
         if (addedPref != null) {
             fDispatcher.addPropertyChangeListener(addedPref.getColorPreferenceKey(), new IPropertyChangeListener() {
                 public void propertyChange(PropertyChangeEvent event) {
                     updateAddedColor(addedPref, store, fDelegate);
                     fDelegate.redraw();
                 }
             });
         }
         if (deletedPref != null) {
             fDispatcher.addPropertyChangeListener(deletedPref.getColorPreferenceKey(), new IPropertyChangeListener() {
                 public void propertyChange(PropertyChangeEvent event) {
                     updateDeletedColor(deletedPref, store, fDelegate);
                     fDelegate.redraw();
                 }
             });
         }
     }

     private Map getAnnotationPreferenceMap() {
         Map annotationPrefs= new HashMap ();
         Iterator iter= fAnnotationPreferences.getAnnotationPreferences().iterator();
         while (iter.hasNext()) {
             AnnotationPreference pref= (AnnotationPreference) iter.next();
             Object type= pref.getAnnotationType();
             annotationPrefs.put(type, pref);
         }
         return annotationPrefs;
     }
     
     private void updateForegroundColor(IPreferenceStore store, IVerticalRulerColumn column) {
         RGB rgb= getColorFromStore(store, FG_COLOR_KEY);
         if (rgb == null)
             rgb= new RGB(0, 0, 0);
         ISharedTextColors sharedColors= getSharedColors();
         if (column instanceof LineNumberRulerColumn)
             ((LineNumberRulerColumn) column).setForeground(sharedColors.getColor(rgb));
     }
     
     private void updateBackgroundColor(IPreferenceStore store, IVerticalRulerColumn column) {
         // background color: same as editor, or system default
 RGB rgb;
         if (store.getBoolean(USE_DEFAULT_BG_KEY))
             rgb= null;
         else
             rgb= getColorFromStore(store, BG_COLOR_KEY);
         ISharedTextColors sharedColors= getSharedColors();
         if (column instanceof LineNumberRulerColumn)
             ((LineNumberRulerColumn) column).setBackground(sharedColors.getColor(rgb));
     }

     private void updateChangedColor(AnnotationPreference pref, IPreferenceStore store, IVerticalRulerColumn column) {
         if (pref != null && column instanceof IChangeRulerColumn) {
             RGB rgb= getColorFromAnnotationPreference(store, pref);
             ((IChangeRulerColumn) column).setChangedColor(getSharedColors().getColor(rgb));
         }
     }

     private void updateAddedColor(AnnotationPreference pref, IPreferenceStore store, IVerticalRulerColumn column) {
         if (pref != null && column instanceof IChangeRulerColumn) {
             RGB rgb= getColorFromAnnotationPreference(store, pref);
             ((IChangeRulerColumn) column).setAddedColor(getSharedColors().getColor(rgb));
         }
     }
     
     private void updateDeletedColor(AnnotationPreference pref, IPreferenceStore store, IVerticalRulerColumn column) {
         if (pref != null && column instanceof IChangeRulerColumn) {
             RGB rgb= getColorFromAnnotationPreference(store, pref);
             ((IChangeRulerColumn) column).setDeletedColor(getSharedColors().getColor(rgb));
         }
     }
     
     private void updateCharacterMode(IPreferenceStore store, IVerticalRulerColumn column) {
         if (column instanceof LineNumberChangeRulerColumn) {
             LineNumberChangeRulerColumn lncrc= (LineNumberChangeRulerColumn) column;
             lncrc.setDisplayMode(store.getBoolean(AbstractDecoratedTextEditorPreferenceConstants.QUICK_DIFF_CHARACTER_MODE));
         }
     }

     private void updateLineNumbersVisibility(IVerticalRulerColumn column) {
         if (column instanceof LineNumberChangeRulerColumn)
             ((LineNumberChangeRulerColumn) column).showLineNumbers(getLineNumberPreference());
     }
     
     private void updateRevisionRenderingMode(IPreferenceStore store, IVerticalRulerColumn column) {
         if (column instanceof IRevisionRulerColumnExtension) {
             String option= store.getString(AbstractDecoratedTextEditorPreferenceConstants.REVISION_RULER_RENDERING_MODE);
             RenderingMode[] modes= { IRevisionRulerColumnExtension.AUTHOR, IRevisionRulerColumnExtension.AGE, IRevisionRulerColumnExtension.AUTHOR_SHADED_BY_AGE };
             for (int i= 0; i < modes.length; i++) {
                 if (modes[i].name().equals(option)) {
                     ((IRevisionRulerColumnExtension) column).setRevisionRenderingMode(modes[i]);
                     return;
                 }
             }
         }
     }
     
     private void updateRevisionAuthorVisibility(IPreferenceStore store, IVerticalRulerColumn column) {
         if (column instanceof IRevisionRulerColumnExtension) {
             boolean show= store.getBoolean(AbstractDecoratedTextEditorPreferenceConstants.REVISION_RULER_SHOW_AUTHOR);
             ((IRevisionRulerColumnExtension) column).showRevisionAuthor(show);
         }
     }
     
     private void updateRevisionIdVisibility(IPreferenceStore store, IVerticalRulerColumn column) {
         if (column instanceof IRevisionRulerColumnExtension) {
             boolean show= store.getBoolean(AbstractDecoratedTextEditorPreferenceConstants.REVISION_RULER_SHOW_REVISION);
             ((IRevisionRulerColumnExtension) column).showRevisionId(show);
         }
     }
     
     private void updateQuickDiffVisibility(IVerticalRulerColumn column) {
         boolean show= getQuickDiffPreference();
         if (show == isShowingChangeInformation())
             return;
         
         if (show)
             installChangeRulerModel(column);
         else
             uninstallChangeRulerModel(column);
     }
     
     /**
      * Returns whether the line number ruler column should be
      * visible according to the preference store settings. Subclasses may override this
      * method to provide a custom preference setting.
      *
      * @return <code>true</code> if the line numbers should be visible
      */
     private boolean getLineNumberPreference() {
         if (fForwarder != null)
             return fForwarder.isLineNumberRulerVisible();
         IPreferenceStore store= getPreferenceStore();
         return store != null ? store.getBoolean(LINE_NUMBER_KEY) : false;
     }
     
     /**
      * Returns whether quick diff info should be visible upon opening an editor
      * according to the preference store settings.
      *
      * @return <code>true</code> if the line numbers should be visible
      */
     private boolean getQuickDiffPreference() {
         if (fForwarder != null)
             return fForwarder.isQuickDiffEnabled();
         IPreferenceStore store= getPreferenceStore();
         boolean setting= store != null ? store.getBoolean(AbstractDecoratedTextEditorPreferenceConstants.QUICK_DIFF_ALWAYS_ON) : false;
         if (!setting)
             return false;
         
         boolean modifiable;
         ITextEditor editor= getEditor();
         if (editor instanceof ITextEditorExtension2) {
             ITextEditorExtension2 ext= (ITextEditorExtension2) editor;
             modifiable= ext.isEditorInputModifiable();
         } else if (editor instanceof ITextEditorExtension) {
             ITextEditorExtension ext= (ITextEditorExtension) editor;
             modifiable= ext.isEditorInputReadOnly();
         } else if (editor != null) {
             modifiable= editor.isEditable();
         } else {
             modifiable= true;
         }
         return modifiable;
     }

     /**
      * Extracts the color preference for the given preference from the given store.
      * If the given store indicates that the default value is to be used, or
      * the value stored in the preferences store is <code>null</code>,
      * the value is taken from the <code>AnnotationPreference</code>'s default
      * color value.
      * <p>
      * The return value is
      * </p>
      *
      * @param store the preference store
      * @param pref the annotation preference
      * @return the RGB color preference, not <code>null</code>
      */
     private static RGB getColorFromAnnotationPreference(IPreferenceStore store, AnnotationPreference pref) {
         String key= pref.getColorPreferenceKey();
         RGB rgb= null;
         if (store.contains(key)) {
             if (store.isDefault(key))
                 rgb= pref.getColorPreferenceValue();
             else
                 rgb= PreferenceConverter.getColor(store, key);
         }
         if (rgb == null)
             rgb= pref.getColorPreferenceValue();
         return rgb;
     }
     
     private static RGB getColorFromStore(IPreferenceStore store, String key) {
         RGB rgb= null;
         if (store.contains(key)) {
             if (store.isDefault(key))
                 rgb= PreferenceConverter.getDefaultColor(store, key);
             else
                 rgb= PreferenceConverter.getColor(store, key);
         }
         return rgb;
     }
     
     /**
      * Ensures that quick diff information is displayed and the quick diff provider is the one with
      * the specified id. If a different quick diff provider is in use, the user may be asked whether
      * he wants to switch.
      *
      * @param diffProviderId the quick diff provider id to use
      * @return <code>true</code> if quick diff could be enabled for the given id,
      * <code>false</code> otherwise
      */
     private boolean ensureQuickDiffProvider(String diffProviderId) {
         if (!isShowingChangeInformation())
             installChangeRulerModel(fDelegate); // FIXME pass provider id

         IAnnotationModel annotationModel= fViewer.getAnnotationModel();
         IAnnotationModel oldDiffer= getDiffer();
         if (oldDiffer == null && annotationModel != null)
             return false; // quick diff is enabled, but no differ? not working for whatever reason

         if (annotationModel == null)
             annotationModel= new AnnotationModel();
         if (!(annotationModel instanceof IAnnotationModelExtension))
             return false;

         QuickDiff util= new QuickDiff();
         Object oldDifferId= util.getConfiguredQuickDiffProvider(oldDiffer);
         if (oldDifferId.equals(diffProviderId)) {
             if (oldDiffer instanceof ILineDifferExtension)
                 ((ILineDifferExtension) oldDiffer).resume();
             return true;
         }

         // Check whether the desired provider is available at all
 IAnnotationModel newDiffer= util.createQuickDiffAnnotationModel(getEditor(), diffProviderId);
         if (util.getConfiguredQuickDiffProvider(newDiffer).equals(oldDifferId)) {
             if (oldDiffer instanceof ILineDifferExtension)
                 ((ILineDifferExtension) oldDiffer).resume();
                 return true;
         }
         
         // quick diff is showing with the wrong provider - ask the user whether he wants to switch
 IPreferenceStore store= EditorsUI.getPreferenceStore();
         if (oldDiffer != null && !store.getString(REVISION_ASK_BEFORE_QUICKDIFF_SWITCH_KEY).equals(MessageDialogWithToggle.ALWAYS)) {
             MessageDialogWithToggle toggleDialog= MessageDialogWithToggle.openOkCancelConfirm(
                     fViewer.getTextWidget().getShell(),
                     RulerMessages.AbstractDecoratedTextEditor_revision_quickdiff_switch_title,
                     RulerMessages.AbstractDecoratedTextEditor_revision_quickdiff_switch_message,
                     RulerMessages.AbstractDecoratedTextEditor_revision_quickdiff_switch_rememberquestion,
                     true,
                     store,
                     REVISION_ASK_BEFORE_QUICKDIFF_SWITCH_KEY);
             if (toggleDialog.getReturnCode() != Window.OK)
                 return false;
         }
         
         IAnnotationModelExtension modelExtension=(IAnnotationModelExtension) annotationModel;
         modelExtension.removeAnnotationModel(IChangeRulerColumn.QUICK_DIFF_MODEL_ID);
         

         modelExtension.addAnnotationModel(IChangeRulerColumn.QUICK_DIFF_MODEL_ID, newDiffer);
         
         if (fDelegate instanceof IChangeRulerColumn)
             ((IChangeRulerColumn) fDelegate).setModel(annotationModel); // picks up the new model attachment

         return true;
     }

     /**
      * Installs the differ annotation model with the current quick diff display.
      *
      * @param column the column to install the model on
      */
     private void installChangeRulerModel(IVerticalRulerColumn column) {
         if (column instanceof IChangeRulerColumn) {
             IAnnotationModel model= getAnnotationModelWithDiffer();
             ((IChangeRulerColumn) column).setModel(model);
             if (model != null) {
                 ISourceViewer viewer= fViewer;
                 if (viewer != null && viewer.getAnnotationModel() == null)
                     viewer.showAnnotations(true);
                 }
         }
     }

     /**
      * Uninstalls the differ annotation model from the current quick diff display.
      *
      * @param column the column to remove the model from
      */
     private void uninstallChangeRulerModel(IVerticalRulerColumn column) {
         if (column instanceof IChangeRulerColumn)
             ((IChangeRulerColumn) column).setModel(null);
         IAnnotationModel model= getDiffer();
         if (model instanceof ILineDifferExtension)
             ((ILineDifferExtension) model).suspend();

         ISourceViewer viewer= fViewer;
         if (viewer != null && viewer.getAnnotationModel() == null)
             viewer.showAnnotations(false);
     }

     /**
      * Returns the annotation model that contains the quick diff annotation model.
      * <p>
      * Extracts the line differ from the displayed document's annotation model. If none can be found,
      * a new differ is created and attached to the annotation model.</p>
      *
      * @return the annotation model that contains the line differ, or <code>null</code> if none could be found or created
      * @see IChangeRulerColumn#QUICK_DIFF_MODEL_ID
      */
     private IAnnotationModel getAnnotationModelWithDiffer() {
         ISourceViewer viewer= fViewer;
         if (viewer == null)
             return null;
         
         IAnnotationModel m= viewer.getAnnotationModel();
         IAnnotationModelExtension model= null;
         if (m instanceof IAnnotationModelExtension)
             model= (IAnnotationModelExtension) m;
         
         IAnnotationModel differ= getDiffer();
         // create diff model if it doesn't
 if (differ == null) {
             IPreferenceStore store= getPreferenceStore();
             if (store != null) {
                 String defaultId= store.getString(AbstractDecoratedTextEditorPreferenceConstants.QUICK_DIFF_DEFAULT_PROVIDER);
                 differ= new QuickDiff().createQuickDiffAnnotationModel(getEditor(), defaultId);
                 if (differ != null) {
                     if (model == null)
                         model= new AnnotationModel();
                     model.addAnnotationModel(IChangeRulerColumn.QUICK_DIFF_MODEL_ID, differ);
                 }
             }
         } else if (differ instanceof ILineDifferExtension2) {
             if (((ILineDifferExtension2) differ).isSuspended())
                 ((ILineDifferExtension) differ).resume();
         } else if (differ instanceof ILineDifferExtension) {
             ((ILineDifferExtension) differ).resume();
         }
         
         return (IAnnotationModel)model;
     }

     /**
      * Extracts the line differ from the displayed document's annotation model. If none can be found,
      * <code>null</code> is returned.
      *
      * @return the line differ, or <code>null</code> if none could be found
      */
     private IAnnotationModel getDiffer() {
         // get annotation model extension
 ISourceViewer viewer= fViewer;
         if (viewer == null)
             return null;

         IAnnotationModel m= viewer.getAnnotationModel();
         if (m == null && fDelegate instanceof IChangeRulerColumn)
             m= ((IChangeRulerColumn)fDelegate).getModel();
         
         if (!(m instanceof IAnnotationModelExtension))
             return null;
         
         IAnnotationModelExtension model= (IAnnotationModelExtension)m;

         // get diff model if it exists already
 return model.getAnnotationModel(IChangeRulerColumn.QUICK_DIFF_MODEL_ID);
     }

     /**
      * Sets the forwarder. Used by {@link AbstractDecoratedTextEditor} to maintain the contract of
      * its {@link AbstractDecoratedTextEditor#createLineNumberRulerColumn} method.
      *
      * @param forwarder the forwarder
      */
     public void setForwarder(ICompatibilityForwarder forwarder) {
         fForwarder= forwarder;
         fDelegate= forwarder.createLineNumberRulerColumn();
     }

     /**
      * Initializes the given line number ruler column from the preference store.
      *
      * @param rulerColumn the ruler column to be initialized
      */
     public void initializeLineNumberRulerColumn(LineNumberRulerColumn rulerColumn) {
         IPreferenceStore store= getPreferenceStore();
         if (store != null) {
             updateForegroundColor(store, rulerColumn);
             updateBackgroundColor(store, rulerColumn);
             updateLineNumbersVisibility(rulerColumn);
             rulerColumn.redraw();
         }
     }

     /**
      * Returns <code>true</code> if the ruler is showing line numbers, <code>false</code> if it
      * is only showing change information.
      *
      * @return <code>true</code> if line numbers are shown, <code>false</code> otherwise
      */
     public boolean isShowingLineNumbers() {
         return fDelegate instanceof LineNumberChangeRulerColumn && ((LineNumberChangeRulerColumn) fDelegate).isShowingLineNumbers();
     }
     
     /**
      * Returns <code>true</code> if the ruler is showing change information, <code>false</code>
      * if it is only showing line numbers.
      *
      * @return <code>true</code> if change information is shown, <code>false</code> otherwise
      */
     public boolean isShowingChangeInformation() {
         return fDelegate instanceof LineNumberChangeRulerColumn && ((LineNumberChangeRulerColumn) fDelegate).isShowingChangeInformation();
     }

     /**
      * Shows revision information on the receiver.
      *
      * @param info the revision information to show
      * @param quickDiffProviderId the id of the corresponding quick diff provider
      */
     public void showRevisionInformation(RevisionInformation info, String quickDiffProviderId) {
         if (!ensureQuickDiffProvider(quickDiffProviderId))
             return;

         if (fDelegate instanceof IRevisionRulerColumn)
             ((IRevisionRulerColumn) fDelegate).setRevisionInformation(info);
     }

     /**
      * Hides revision information.
      */
     public void hideRevisionInformation() {
         if (fDelegate instanceof IRevisionRulerColumn)
             ((IRevisionRulerColumn) fDelegate).setRevisionInformation(null);
     }
     
     /**
      * Returns <code>true</code> if the ruler is showing revision information, <code>false</code>
      * if it is only showing line numbers.
      *
      * @return <code>true</code> if revision information is shown, <code>false</code> otherwise
      */
     public boolean isShowingRevisionInformation() {
         if (fDelegate instanceof LineNumberChangeRulerColumn)
             return ((LineNumberChangeRulerColumn) fDelegate).isShowingRevisionInformation();
         return false;
     }

     /**
      * Returns the selection provider of the revision column, <code>null</code> if none is
      * available.
      *
      * @return the revision selection provider
      */
     public ISelectionProvider getRevisionSelectionProvider() {
         if (fDelegate instanceof IRevisionRulerColumnExtension)
             return ((IRevisionRulerColumnExtension) fDelegate).getRevisionSelectionProvider();
         return null;
     }
 }

